/* eslint-disable no-unused-vars */

class Observer {
  constructor (data, vm) {
    this.vm = vm
    this.data = data
    this.walk(data)
  }
  walk (data) {
    Object.keys(data).forEach((key) => {
      if (typeof data[key] !== 'function') {
        this.defineReactive(data, key, data[key])
      } else {
        this.defineReactive(data, key, data[key].bind(this.vm))
      }
    })
  }
  defineReactive (data, key, val) {
    let dep = new Dep()
    if (typeof data[key] !== 'function') {
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: false,
        get: function () {
          if (Dep.target) {
            dep.addSub(Dep.target)
          }
          return val
        },
        set: function (newVal) {
          if (newVal === val) {
            return false
          }
          val = newVal
          dep.notify()
        }
      })
    } else {
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: false,
        get: function () {
          return val()
        },
        set: function () {
          throw new Error('computed property no setter')
        }
      })
    }
  }
}

class Dep {
  constructor () {
    this.subs = []
  }
  addSub (sub) {
    this.subs.push(sub)
  }
  notify () {
    this.subs.forEach((sub) => {
      sub.update()
    })
  }
}
(function () {
  return Dep
})()

function observer (data, vm) {
  if (!data || typeof data !== 'object') {
    return false
  }
  return new Observer(data, vm)
}
